package models

import (
	"errors"
	"log"
	"strconv"

	"github.com/unknwon/com"
	"gorm.io/gorm"
)

type SysBaseMenu struct {
	ModelBase
	MenuLevel     uint                              `json:"-"`
	ParentID      string                            `json:"parentId" form:"parentId" gorm:"comment:'父菜单ID';default:'0'"` // 父菜单ID
	Path          string                            `json:"path" gorm:"comment:'路由path'"`                                // 路由path
	Name          string                            `json:"name" gorm:"comment:'路由name'"`                                // 路由name
	Hidden        bool                              `json:"hidden" gorm:"comment:'是否在列表隐藏'"`                             // 是否在列表隐藏
	Component     string                            `json:"component" gorm:"comment:'对应前端文件路径'"`                         // 对应前端文件路径
	Sort          int                               `json:"sort" gorm:"comment:'排序标记'"`                                  // 排序标记
	Meta          `json:"meta" gorm:"comment:附加属性"` // 附加属性
	SysAuthoritys []SysAuthority                    `json:"authoritys" gorm:"many2many:sys_authority_menus;"`
	Children      []SysBaseMenu                     `json:"children" gorm:"-"`
	Parameters    []SysBaseMenuParameters           `json:"parameters"`
	MenuBtn       []SysBaseMenuBtn                  `json:"menuBtn"`
}

func (SysBaseMenu) TableName() string {
	return "sys_base_menus"
}

type Meta struct {
	KeepAlive   bool   `json:"keepAlive" gorm:"comment:'是否缓存'"`           // 是否缓存
	DefaultMenu bool   `json:"defaultMenu" gorm:"comment:'是否是基础路由（开发中）'"` // 是否是基础路由（开发中）
	Title       string `json:"title" gorm:"comment:'菜单名'"`                // 菜单名
	Icon        string `json:"icon" gorm:"comment:'菜单图标'"`                // 菜单图标
	CloseTab    bool   `json:"closeTab" gorm:"comment:'自动关闭tab'"`         // 自动关闭tab
}

type SysBaseMenuParameters struct {
	ModelBase
	SysBaseMenuID int
	Type          string `json:"type" gorm:"comment:'地址栏携带参数为params还是query'"` // 地址栏携带参数为params还是query
	Key           string `json:"key" gorm:"comment:'地址栏携带参数的key'"`            // 地址栏携带参数的key
	Value         string `json:"value" gorm:"comment:'地址栏携带参数的值'"`            // 地址栏携带参数的值
}

type AddMenuAuthorityInfo struct {
	Menus       []int `json:"menus"`
	AuthorityId uint  `json:"authorityId"` // 角色ID
}

//@function: AddBaseMenu
//@description: 添加基础路由
//@param: menu model.SysBaseMenu
//@return: err error
func AddMenuBase(menu SysBaseMenu) error {
	if !errors.Is(db.Where("name = ?", menu.Name).First(&SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
		return errors.New("菜单名称已经存在，无法添加")
	}
	return db.Create(&menu).Error
}

func UpdateMenuBase(menu SysBaseMenu) (err error) {
	var oldMenu SysBaseMenu
	upDateMap := make(map[string]interface{})
	upDateMap["keep_alive"] = menu.KeepAlive
	upDateMap["close_tab"] = menu.CloseTab
	upDateMap["default_menu"] = menu.DefaultMenu
	upDateMap["parent_id"] = menu.ParentID
	upDateMap["path"] = menu.Path
	upDateMap["name"] = menu.Name
	upDateMap["hidden"] = menu.Hidden
	upDateMap["component"] = menu.Component
	upDateMap["title"] = menu.Title
	// upDateMap["active_name"] = menu.ActiveName
	upDateMap["icon"] = menu.Icon
	upDateMap["sort"] = menu.Sort

	err = db.Transaction(func(tx *gorm.DB) error {
		db := tx.Where("id = ?", menu.ID).Find(&oldMenu)
		if oldMenu.Name != menu.Name {
			if !errors.Is(tx.Where("id <> ? AND name = ?", menu.ID, menu.Name).First(&SysBaseMenu{}).Error, gorm.ErrRecordNotFound) {
				log.Println("存在相同name修改失败")
				return errors.New("存在相同name修改失败")
			}
		}
		txErr := tx.Unscoped().Delete(&SysBaseMenuParameters{}, "sys_base_menu_id = ?", menu.ID).Error
		if txErr != nil {
			return txErr
		}
		txErr = tx.Unscoped().Delete(&SysBaseMenuBtn{}, "sys_base_menu_id = ?", menu.ID).Error
		if txErr != nil {
			return txErr
		}
		if len(menu.Parameters) > 0 {
			for k := range menu.Parameters {
				menu.Parameters[k].SysBaseMenuID = menu.ID
			}
			txErr = tx.Create(&menu.Parameters).Error
			if txErr != nil {
				return txErr
			}
		}

		if len(menu.MenuBtn) > 0 {
			for k := range menu.MenuBtn {
				menu.MenuBtn[k].SysBaseMenuID = uint(menu.ID)
			}
			txErr = tx.Create(&menu.MenuBtn).Error
			if txErr != nil {
				// global.GVA_LOG.Debug(txErr.Error())
				return txErr
			}
		}

		txErr = db.Updates(upDateMap).Error
		if txErr != nil {
			// global.GVA_LOG.Debug(txErr.Error())
			return txErr
		}
		return nil
	})
	return err
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetBaseMenuById
//@description: 返回当前选中menu
//@param: id int64
//@return: err error, menu model.SysBaseMenu
func GetBaseMenuByID(id int64) (err error, menu SysBaseMenu) {
	err = db.Preload("MenuBtn").Preload("Parameters").Where("id = ?", id).First(&menu).Error
	return
}

//@function: DeleteBaseMenu
//@description: 删除基础路由
//@param: id int64
//@return: err error
func DeleteBaseMenu(id int64) (err error) {
	err = db.Preload("Parameters").Where("parent_id = ?", id).First(&SysBaseMenu{}).Error
	if err != nil {
		var menu SysBaseMenu
		// db := db.Preload("SysAuthoritys").Where("id = ?", id).First(&menu).Delete(&menu)
		db := db.Where("id = ?", id).Delete(&menu)
		// err = db.Delete(&SysBaseMenuParameters{}, "sys_base_menu_id = ?", id).Error
		if len(menu.SysAuthoritys) > 0 {
			err = db.Model(&menu).Association("SysAuthoritys").Delete(&menu.SysAuthoritys)
		} else {
			err = db.Error
		}
	} else {
		return errors.New("此菜单存在子菜单不可删除")
	}
	return err
}

//@function: GetMenuAuthority
//@description: 查看当前角色树
//@param: info *request.GetAuthorityId
//@return: err error, menus []model.SysMenu
func GetMenuAuthority(authorityID int64) (err error, menus []SysMenu) {
	err = db.Where("authority_id = ? ", authorityID).Order("sort").Find(&menus).Error
	//sql := "SELECT authority_menu.keep_alive,authority_menu.default_menu,authority_menu.created_at,authority_menu.updated_at,authority_menu.deleted_at,authority_menu.menu_level,authority_menu.parent_id,authority_menu.path,authority_menu.`name`,authority_menu.hidden,authority_menu.component,authority_menu.title,authority_menu.icon,authority_menu.sort,authority_menu.menu_id,authority_menu.authority_id FROM authority_menu WHERE authority_menu.authority_id = ? ORDER BY authority_menu.sort ASC"
	//err = global.GVA_DB.Raw(sql, authorityId).Scan(&menus).Error
	return err, menus
}

//@function: AddMenuAuthority
//@description: 为角色增加menu树
//@param: menus []model.SysBaseMenu, authorityId string
//@return: err error

func AddMenuAuthority(menus []SysBaseMenu, authorityId string) (err error) {
	var auth SysAuthority
	auth.AuthorityId = uint(com.StrTo(authorityId).MustInt())
	auth.SysBaseMenus = menus
	err = SetMenuAuthority(&auth)
	return err
}

//@function: GetBaseMenuTree
//@description: 获取基础路由树
//@return: err error, menus []model.SysBaseMenu
func GetBaseMenuTree() (err error, menus []SysBaseMenu) {
	err, treeMap := getBaseMenuTreeMap()
	if err != nil {
		return err, menus
	}

	menus = treeMap["0"]
	for i := 0; i < len(menus); i++ {
		err = getBaseChildrenList(&menus[i], treeMap)
	}
	return err, menus
}

//@function: getBaseMenuTreeMap
//@description: 获取路由总树map
//@return: err error, treeMap map[string][]model.SysBaseMenu

func getBaseMenuTreeMap() (err error, treeMap map[string][]SysBaseMenu) {
	var allMenus []SysBaseMenu
	treeMap = make(map[string][]SysBaseMenu)
	err = db.Order("sort").Preload("Parameters").Find(&allMenus).Error
	for _, v := range allMenus {
		if v.ParentID == "" {
			v.ParentID = "0"
		}
		treeMap[v.ParentID] = append(treeMap[v.ParentID], v)
	}
	return err, treeMap
}

//@author: [piexlmax](https://github.com/piexlmax)
//@function: getBaseChildrenList
//@description: 获取菜单的子菜单
//@param: menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu
//@return: err error

func getBaseChildrenList(menu *SysBaseMenu, treeMap map[string][]SysBaseMenu) (err error) {
	menu.Children = treeMap[strconv.Itoa(int(menu.ID))]
	for i := 0; i < len(menu.Children); i++ {
		err = getBaseChildrenList(&menu.Children[i], treeMap)
	}
	return err
}
